unit HardComputerPlayer;

interface

uses Player, EasyComputerPlayer, Board, BoardArray, Types, Dialogs, SysUtils, Pawn;

type

 THardComputerPlayer = class(TEasyComputerPlayer)
 private
  function ObtainPoints(Board: TBoard; PosSource, PosTarget: TArrayPoint): Integer;
  function GetNeighbourhoodListWhereMovesAvailable(myBoard: TBoard; Pos: TArrayPoint): TPointList;
 public
  procedure PerfromAMove(Board: TObject); override;
  function IsOwnMovable: Boolean; override;
  constructor Create;
 end;

implementation

constructor THardComputerPlayer.Create;
begin
 inherited Create;
 Avatar.LoadFromFile('graph/hard_robot.bmp');
end;

procedure SimulateGravity(GameBoard: TBoardArray);
var
 i,j: integer;
 Dimension: Integer;
 isOK: boolean;
begin

 Dimension := Length(GameBoard);

 for i := 0 to Dimension-1 do
  begin
   isOK := false;

   while not isOK do
    begin
     j := Dimension-1;
     isOK := true;

      while (j > 0) do
       begin
        if (GameBoard[i, j] = nil) and (GameBoard[i, j-1] <> nil) then
         begin
          GameBoard[i, j] := GameBoard[i, j-1];
          GameBoard[i, j-1] := nil;
          isOK := false;
         end;
        dec(j);
       end;

    end;

  end;

end;

procedure Swap(Board: TBoardArray; PosSource, PosTarget: TArrayPoint);
var
 temp: TPawn;
begin
 temp := Board[PosSource.GetX, PosSource.GetY];
 Board[PosSource.GetX, PosSource.GetY] := Board[PosTarget.GetX, Postarget.GetY];
 Board[PosTarget.GetX, Postarget.GetY] := temp;
end;

function DetermineToRemoveNeighbourhoods(GameBoard: TBoardArray; i: integer; j: integer): TRemoveRanges;
var
 range1, range2, range3, range4, Dimension: integer;
 output: TRemoveRanges;
begin

 Dimension := Length(GameBoard);
 range1 := i-1;
 range2 := i+1;

 while (range1 >= 0) and (GameBoard[i,j] <> nil) and (GameBoard[range1,j] <> nil) and (GameBoard[range1+1,j] <> nil) and (GameBoard[range1, j].IsTheSameColor(GameBoard[i,j])) and (GameBoard[range1, j].IsTheSameColor(GameBoard[range1+1,j])) do
  dec(range1);

 while (range2 < Dimension) and (GameBoard[i,j] <> nil) and (GameBoard[range2, j] <> nil) and (GameBoard[range2-1,j] <> nil) and (GameBoard[range2, j].IsTheSameColor(GameBoard[i,j])) and (GameBoard[range2, j].IsTheSameColor(GameBoard[range2-1,j])) do
  inc(range2);

 range3 := j-1;
 range4 := j+1;

 while (range3 >= 0) and (GameBoard[i,j] <> nil) and (GameBoard[i, range3] <> nil) and (GameBoard[i,range3+1] <> nil) and (GameBoard[i, range3].IsTheSameColor(GameBoard[i,j])) and (GameBoard[i, range3].IsTheSameColor(GameBoard[i,range3+1])) do
  dec(range3);

 while (range4 < Dimension) and (GameBoard[i,j] <> nil) and (GameBoard[i, range4] <> nil) and (GameBoard[i,range4-1] <> nil) and (GameBoard[i, range4].IsTheSameColor(GameBoard[i,j])) and (GameBoard[i, range4].IsTheSameColor(GameBoard[i,range4-1])) do
  inc(range4);


 if (GameBoard[range1+1, j] <> nil) and (GameBoard[range2-1, j] <> nil) and (not GameBoard[range1+1, j].IsTheSameColor(GameBoard[range2-1, j])) then
  range1 := range2;

 if (GameBoard[i, range4-1] <> nil) and (GameBoard[i, range3+1] <> nil) and (not GameBoard[i, range3+1].IsTheSameColor(GameBoard[i, range4-1])) then
  range3 := range4;

 output.Left := range1;
 output.Right := range2;
 output.Bottom := range3;
 output.Top := range4;

 Result := output;
end;

procedure RemoveRange(GameBoard: TBoardArray; Range: TRemoveRanges; Player: TPlayer; i: integer; j: integer; MovesList: TPointList);
var
 range1, range2, range3, range4, k: Integer;
 tempPoint: TArrayPoint;
begin

 range1 := Range.Left;
 range2 := Range.Right;
 range3 := Range.Bottom;
 range4 := Range.Top;

  if (range2 - range1 - 1 >= 3) then
 begin
  k := range1+1;
  while k < range2 do
    begin
     if (GameBoard[k,j] <> nil) then
      begin
       GameBoard[k,j].OnDestruct(Player);
       GameBoard[k,j] := nil;
       tempPoint := TArrayPoint.Create(k,j);
       if not MovesList.ContainsUseEquals(tempPoint) then
        MovesList.Add(tempPoint) else tempPoint.Free;
      end;
     inc(k);
    end;
 end;

 if (range4 - range3 - 1 >= 3) then
 begin
  k := range3+1;

   while k < range4 do
   begin
    if (GameBoard[i,k] <> nil) then
     begin
      GameBoard[i,k].OnDestruct(Player);
      GameBoard[i,k] := nil;
      tempPoint := TArrayPoint.Create(i,k);
      if not MovesList.ContainsUseEquals(tempPoint) then
       MovesList.Add(tempPoint) else tempPoint.Free;
     end;
    inc(k);
   end;
 end;
end;

function THardComputerPlayer.ObtainPoints(Board: TBoard; PosSource, PosTarget: TArrayPoint): integer;
var
 tempGameBoard: TBoardArray;
 i, j: Integer;
 Moves: TPointList;
 item1Range: TRemoveRanges;
 myPointX, myPointY, Count, MaxCount: integer;
 CurrentPlayer: TPlayer;
begin

 tempGameBoard := Board.GetBoardArray;
 CurrentPlayer := TPlayer.Create;
 Swap(tempGameBoard, PosSource, PosTarget);

 Moves := TPointList.Create;
 Moves.OwnsObjects := true;
 Moves.Add(TArrayPoint.Create(PosSource.GetX, PosTarget.GetY));
 Moves.Add(TArrayPoint.Create(PosTarget.GetX, PosTarget.GetY));

 while Moves.Count > 0 do
  begin
    myPointX := Moves[0].GetX;
    myPointY := Moves[0].GetY;
    Moves.Delete(0);

    item1Range := DetermineToRemoveNeighbourhoods(tempGameBoard, myPointX, myPointY);
    RemoveRange(tempGameBoard, item1Range, CurrentPlayer, myPointX, myPointY, Moves);

    SimulateGravity(tempGameBoard);
  end;

 Result := CurrentPlayer.GetPoints;
end;

function THardComputerPlayer.GetNeighbourhoodListWhereMovesAvailable(myBoard: TBoard; Pos: TArrayPoint): TPointList;
var
 output: TPointList;
 x,y: Integer;
begin
 output := TPointList.Create;
 x := Pos.GetX;
 y := Pos.GetY;

 if myBoard.IsLeftNeighbourhood(x,y) then
  output.Add(TArrayPoint.Create(x-1,y));

 if myBoard.IsRightNeighbourhood(x,y) then
  output.Add(TArrayPoint.Create(x+1, y));

 if myBoard.IsTopNeighbourhood(x,y) then
  output.Add(TArrayPoint.Create(x, y-1));

 if myBoard.IsBottomNeighbourhood(x,y) then
  output.Add(TArrayPoint.Create(x, y+1));
  
 Result := output;
end;

procedure THardComputerPlayer.PerfromAMove(Board: TObject);
var
 myBoard: TBoard;
 MovesList, NeighbourhoodList: TPointList;
 BestMoveSource, BestMoveTarget, tempPoint1, tempPoint2: TArrayPoint;
 BestScoreForMove, tempPoints: Integer;
 i,j: integer;
begin
 myBoard := TBoard(Board);

 MovesList := GetAvailableMoves(myBoard);

 BestMoveSource := nil;
 BestMoveTarget := nil;
 BestScoreForMove := 0;
 NeighbourhoodList := nil;

 if MovesList.Count > 0 then
  begin
   for i := 0 to MovesList.Count-1 do
    begin
     if NeighbourhoodList <> nil then NeighbourhoodList := nil;
      NeighbourhoodList := GetNeighbourhoodListWhereMovesAvailable(myBoard, MovesList[i]);
      
     for j := 0 to NeighbourhoodList.Count-1 do
      begin
       tempPoints := ObtainPoints(myBoard, MovesList[i], NeighbourhoodList[j]);

       if (tempPoints > BestScoreForMove) then
        begin
         BestScoreForMove := tempPoints;
         BestMoveSource := MovesList[i];
         BestMoveTarget := NeighbourhoodList[j];
        end;

      end;

     end;
   myBoard.PerformAMove(Point(BestMoveSource.GetX, BestMoveSource.GetY), Point(BestMoveTarget.GetX, BestMoveTarget.GetY), Self);
  end;

end;

function THardComputerPlayer.IsOwnMovable: Boolean;
begin
 Result := True;
end;

end.
